package com.apes.fn.fico.reimbursement.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.crm.business.repository.InsuranceCompanyRepository;
import com.apes.fn.business.fixedLoss.modle.LossReceivedItem;
import com.apes.fn.fico.reimbursement.model.*;
import com.apes.fn.fico.reimbursement.repository.*;
import com.apes.fn.scm.insuranceRules.InsuranceRuleRepository;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.*;
import com.apes.importData.model.ImportControls;
import com.apes.importData.respository.ImportControlsRepository;
import com.apes.scm.masterdata.finance.model.BankAccount;
import com.apes.scm.masterdata.finance.repository.BankAccountRepository;
import com.apes.scm.masterdata.frame.repository.DeptRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 银行流水
 * Created by lishikun on 2021/1/29 11:33
 */
@Service("transactionFlowService")
public class TransactionFlowService {
    @Autowired
    TransactionFlowRepository transactionFlowRepository;

    @Autowired
    ImportControlsRepository importControlsRepository;

    @Autowired
    TransactionFlowDetailsRepository transactionFlowDetailsRepository;

    /**
     * 保存
     *
     * @param request
     * @return
     */
    public TransactionFlow save(SimpleRequest request) {
        TransactionFlow transactionFlow = request.getO(TransactionFlow.class);
        return saveTransactionFlow(transactionFlow);
    }

    /**
     * 保存方法
     *
     * @param transactionFlow
     * @return
     */
    public TransactionFlow saveTransactionFlow(TransactionFlow transactionFlow) {
        transactionFlow.setState("create");
        TransactionFlow flow = transactionFlowRepository.saveAndFlush(transactionFlow);
        if (transactionFlowRepository.findAll(JpaDsl.toCriteriaByEq("transactionFlowNum", transactionFlow.getTransactionFlowNum()))
                .stream().filter(t->!t.getState().equals("stop")).collect(Collectors.toList()).size() != 1) {
            throw new RuntimeException(String.format("交易流水号存在历史数据,不允许重复！[%s]", transactionFlow.getTransactionFlowNum()));
        }
        return flow;
    }

    /**
     * 审核
     *
     * @param request
     * @return
     */
    public TransactionFlow approve(SimpleRequest request) {
        TransactionFlow transactionFlow = request.getO(TransactionFlow.class);
        transactionFlow.setState("approve");
        transactionFlow.setApproveDate(new Date());
        transactionFlow.setApproveId(request.getPerson());
        return transactionFlowRepository.saveAndFlush(transactionFlow);
    }

    /**
     * 完成
     *
     * @param request
     * @return
     */
    public void byGenerateLossReceivedItem(SimpleRequest request){
        LossReceivedItem lossReceivedItem = request.getO(LossReceivedItem.class);

        TransactionFlowDetails transactionFlowDetails = createDetails(lossReceivedItem);

        decideState(transactionFlowDetails);
    }

    private TransactionFlowDetails createDetails(LossReceivedItem lossReceivedItem){
        TransactionFlow transactionFlow = transactionFlowRepository.findByTransactionFlowNum(lossReceivedItem.getSerialNumber());
        TransactionFlowDetails transactionFlowDetails = new TransactionFlowDetails();
        transactionFlowDetails.setTransactionFlow(transactionFlow);
        transactionFlowDetails.setTransactionDate(transactionFlow.getTransactionDate());
        transactionFlowDetails.setTransactionFlowNum(transactionFlow.getTransactionFlowNum());
        transactionFlowDetails.setReceiptAmount(transactionFlow.getReceiptAmount());
        transactionFlowDetails.setAmount(transactionFlow.getAmount());
        transactionFlowDetails.setUseDate(new Date());
        transactionFlowDetails.setSourceBusinessType(lossReceivedItem.getLossQuotation().getBusinessTypeName());
        transactionFlowDetails.setSourceBusinessKey(lossReceivedItem.getLossQuotation().getId());
        transactionFlowDetails.setUsedAmount(lossReceivedItem.getReceivedAmount());
        return transactionFlowDetailsRepository.saveAndFlush(transactionFlowDetails);
    }

    private void decideState(TransactionFlowDetails transactionFlowDetails){
        TransactionFlow transactionFlow = transactionFlowDetails.getTransactionFlow();
        transactionFlow.setUsedAmount(transactionFlow.getUsedAmount() + transactionFlowDetails.getUsedAmount());
        double amount = transactionFlow.getAmount();
        double receiptAmount = transactionFlow.getReceiptAmount();
        double usedAmount = transactionFlow.getUsedAmount();
        if(amount != 0 && (amount - usedAmount) == 0){
            transactionFlow.setState("done");
        } else if(receiptAmount != 0 && (receiptAmount - usedAmount) == 0 ){
            transactionFlow.setState("done");
        }
        transactionFlowRepository.saveAndFlush(transactionFlow);
    }


    /**
     * 删除
     *
     * @param request
     * @return
     */
    public String delete(SimpleRequest request) {
        TransactionFlow transactionFlow = request.getO(TransactionFlow.class);
        transactionFlowRepository.delete(transactionFlow);
        return "删除成功";
    }

    /**
     * 终止
     *
     * @param request
     * @return
     */
    public TransactionFlow stop(SimpleRequest request) {
        TransactionFlow transactionFlow = request.getO(TransactionFlow.class);
        transactionFlow.setState("stop");
        return transactionFlowRepository.saveAndFlush(transactionFlow);
    }

    /**
     * 批量创建
     *
     * @param request
     * @return
     */
    public Map batchCreate(SimpleRequest request) {
        List<TransactionFlow> transactionFlows = request.getJO().getJSONArray("transactionFlows")
                .stream().map(o -> Tools.map(o, TransactionFlow.class)).collect(Collectors.toList());
        List<TransactionFlow> flowList = new ArrayList<>();
        transactionFlows.forEach(transactionFlow ->{
            if(transactionFlow.getAmount() != 0 && transactionFlow.getReceiptAmount() != 0)
                throw new RuntimeException("交易流水号:" + transactionFlow.getTransactionFlowNum() + "不能同时存在付款金额和收款金额");
            flowList.add(saveTransactionFlow(transactionFlow));

        });
        return MapUtil.mapper("transactionFlows", flowList);
    }

    /**
     * 批量审核
     *
     * @param request
     * @return
     */
    public Map batchApproval(SimpleRequest request) {
        JSONArray transactionFlows = request.getJO().getJSONArray("transactionFlows");
        if (transactionFlows == null) return new HashMap();
        List<TransactionFlow> transactionFlowList = transactionFlows.stream()
                .map(o -> Tools.map(o, TransactionFlow.class)).collect(Collectors.toList());
        for (TransactionFlow transactionFlow : transactionFlowList) {
            transactionFlow.setState("approve");
            transactionFlow.setApproveDate(new Date());
            transactionFlow.setApproveId(request.getPerson());
            try {
                transactionFlowRepository.saveAndFlush(transactionFlow);
            } catch (Exception e) {
                throw new RuntimeException(String.format("交易流水[%s]已被更改！请重新查询后操作", transactionFlow.getId()));
            }
        }
        return MapUtil.mapper("transactionFlows", transactionFlowList);
    }

    public List findOneToList(SimpleRequest request) {
        JSONObject object = request.getJO();
        JSONArray jsonArray = SpringManager.getBean(TransactionFlowDao.class).queryAllTransactionFlow(object);
        List result = jsonArray.stream().map(
                item -> {
                    JSONObject jsonObject = (JSONObject) item;
                    String payment_account_id = jsonObject.getString("PAYMENT_ACCOUNT_ID");
                    String dept_id = jsonObject.getString("DEPT_ID");
                    String insurance_company_id = jsonObject.getString("INSURANCE_COMPANY_ID");
                    if(payment_account_id == null)  payment_account_id = "";
                    if(dept_id == null)  dept_id = "";
                    if(insurance_company_id == null)  insurance_company_id = "";
                    BankAccount bankAccount = SpringManager.getBean(BankAccountRepository.class).findOne(payment_account_id);
                    String accountId = "";
                    if(bankAccount != null) {
                        accountId = bankAccount.getAccountId();
                    }
                    return MapUtil.mapper(
                            "id", jsonObject.getString("ID"),
                            "transactionDate", jsonObject.getString("TRANSACTION_DATE"),
                            "transactionFlowNum", jsonObject.getString("TRANSACTION_FLOW_NUM"),
                            "receiptAmount", jsonObject.getString("RECEIPT_AMOUNT"),
                            "amount", jsonObject.getDouble("AMOUNT"),
                            "dept", SpringManager.getBean(DeptRepository.class).findOne(dept_id),
                            "carLicense",jsonObject.getString("CAR_LICENSE"),
                            "insuranceCompany", SpringManager.getBean(InsuranceCompanyRepository.class).findOne(insurance_company_id),
                            "remark", jsonObject.getString("REMARK"),
                            "bankAccount",bankAccount ,
                            "accountId", accountId,
                            "counterpartyAccountName", jsonObject.getString("COUNTERPARTY_ACCOUNT_NAME"),
                            "counterpartyAccountNum", jsonObject.getString("COUNTERPARTY_ACCOUNT_NUM"),
                            "source", jsonObject.getString("SOURCE"),
                            "sourceBusinessType", jsonObject.getString("SOURCE_BUSINESS_TYPE"),
                            "used", jsonObject.getString("USED"),
                            "createDate", jsonObject.getString("CREATE_DATE"),
                            "state", jsonObject.getString("STATE")
                    );
                }
        ).collect(Collectors.toList());
        return result;
    }

    /**
     * 获取未被使用的流水号
     *
     * @param request
     * @return
     */
    public Page<TransactionFlow> findAllNotUsed(SimpleRequest request) {
        List<String> transactionFlowItems = new ArrayList<>();
        String value = request.get("key");
        Page<TransactionFlow> flowPage = transactionFlowRepository.findAllNotUsed(value, PageRequest.of(request.get("page"), request.get("size")));
        return flowPage;
    }

    /**
     * 交易流水导入
     *
     * @param request
     * @return
     */
    public JSONObject beImportTransaction(SimpleRequest request) {
        JSONObject importData = request.get("importData");//Excel信息
        List<Map> data = request.get("data");                     //获取页面标签信息
        JSONObject exclObject = new JSONObject();                  //返回值

        //以headerName为key 存储cellEditor,cellEditorParams,downFormatter,field
        Map<String, Map> header = data.stream().collect(Collectors.toMap(x -> x.get("name").toString(), x -> x));

        //传来的Key 未知，遍历
        importData.forEach((k, v) -> {
            //获得value===Excel内容
            JSONArray excl = (JSONArray) importData.get(k); //excl原值
            JSONArray result = new JSONArray();         //返回值

            //map存放匹配过的数据------
            Map<Object, Object> resultMap = new HashMap();

            JSONArray firstLine = (JSONArray) excl.get(0);      //firstLine==Excel表头
            result.add(firstLine);
            //遍历value===遍历excel每一行

            String transactionFlowNum = "";
            int count = 0;
            for (int i = 0; i < excl.size() ; i++) {
                if (i == 0) {  //跳过表头
                    continue;
                }
                int receiptAmount = 0;
                int amount = 0;
                JSONArray currentLine = (JSONArray) excl.get(i); //获取当前行
                if (currentLine.size() == 0) {
                    continue; //跳过空白行
                }
                for (int j = 0; j < currentLine.size(); j++) {     //遍历每一行内容
                    Object line = currentLine.get(j);//行内容
                    if("交易日期".equals(((JSONArray) excl.get(0)).getString(j)) && currentLine.get(j).toString().length() <= 5) {
                        Integer integerDate = (Integer) currentLine.get(j);
                        String date = LocalDate.of(1900, Month.JANUARY, 1).plusDays(integerDate).toString();
                        currentLine.set(j,date);
                    }

                    if("交易流水号".equals(((JSONArray) excl.get(0)).getString(j)) && transactionFlowNum.equals(line.toString())) throw new RuntimeException("交易流水号不能重复");
                    if("交易流水号".equals(((JSONArray) excl.get(0)).getString(j)) && count != 1){
                        transactionFlowNum = line.toString();
                        count = 1;
                    }
                    if("收款金额".equals(((JSONArray) excl.get(0)).getString(j)) && line != null) receiptAmount =  (Integer)line;
                    if("付款金额".equals(((JSONArray) excl.get(0)).getString(j)) && line != null) amount =  (Integer)line;
                    if(receiptAmount > 0 && amount > 0) throw new RuntimeException("不允许同时存在收款金额和付款金额");
                    //如果当前单元格内容已经存在于map中，直接拿取
                    if (resultMap.containsKey(firstLine.get(j) + ":" + line)) {  //KEY:Excel列名+单元格内容
                        currentLine.set(j, resultMap.get(firstLine.get(j) + ":" + line));
                        continue;
                    }

                    //获取表头标题对应的field值
                    if (!header.containsKey(firstLine.getString(j))) {
                        throw new RuntimeException(String.format("Excl表头中[%s]在界面中无对照！请检查！", firstLine.getString(j)));
                    }
                    String field = (String) header.get(firstLine.getString(j)).get("field");
                    if (field.equals("operation")) {
                        currentLine.set(j, "");
                        continue; //跳过序号
                    }

                    //获取实体的领域类
                    String domainTypeName = JpaUtil.getAllDomainType().stream()
                            .filter(s -> ReflectUtil.getClass(s).getSimpleName().equalsIgnoreCase(field))
                            .findFirst().orElse(null);

                    if (domainTypeName == null) {                       //获取不到实体的领域类
                        //查询对照表
                        ImportControls importControls = importControlsRepository.findAllByBizField(field);
                        if (importControls != null)
                            domainTypeName = JpaUtil.getDomainClass(importControls.getBusinessType().getId()).getName();
                    }
                    if (domainTypeName != null) {                       //基本数据类型,不做改变；或者未录入对照表
                        CustomRepository repository = JpaUtil.getRepository(domainTypeName);

                        Object value = null;

                        try {
                            Serializable id = JpaUtil.getId(domainTypeName, line.toString());
                            value = repository.findOne(id);
                        } catch (Exception e) {
                        }

                        //检查是否枚举属性
                        if (domainTypeName.equals("com.apes.scm.masterdata.enumeration.model.Enumeration")) {
                            value = repository.findOne(JpaDsl.toCriteriaByEq("description", line, "type.id", field)).orElse(null);
                        }

                        //检查是否当事人角色属性
                        if (value == null && domainTypeName.equals("com.apes.scm.masterdata.party.model.PartyRole")) {
                            try {
                                char[] chars = field.toCharArray();
                                chars[0] = chars[0] ^= 32;
                                value = repository.findOne(JpaDsl.toCriteriaByEq("party.name", line, "role.id", String.valueOf(chars))).orElse(null);
                            } catch (Exception e) {
                                throw new RuntimeException(firstLine.getString(j) + ":" + line + " 在对应表中存在重名或无法根据当前条件获取，导入终止，建议检查此列数据，修改为ID导入。");
                            }

                        }

                        if (value == null) {
                            try {
                                value = repository.findOne(JpaDsl.toCriteriaByEq("name", line)).orElse(null);

                            } catch (Exception e) {
                                throw new RuntimeException(firstLine.getString(j) + ":" + line + " 在对应表中存在重名或无法根据当前条件获取，导入终止，建议检查此列数据，修改为ID导入。");
                            }
                        }

                        if (value == null) {
                            throw new RuntimeException(firstLine.getString(j) + ":" + line + " 命名不规范或者在对照表中不存在，导入终止，建议检查此列记录。");
                        }

                        //有且只有此一行记录,修改Excel内容
                        currentLine.set(j, value);
                        //存入map
                        resultMap.put(firstLine.get(j) + ":" + line, value); //KEY:Excel列名+单元格内容
                    } else { //基本数据类型
                        if (currentLine.get(j) == null) {  // 为空时传回''
                            (currentLine).set(j, "");
                        }
                    }
                }
                result.add(currentLine);
                exclObject.put(k, result);
            }
        });

        return exclObject;
    }

}
